home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************
- * *
- * Module: gstream.c (part of gstream package) *
- * *
- * Version: 0.13 *
- * *
- * Author: Simon Proven *
- * *
- * Owner: Simon Proven 1993 *
- * *
- * Purpose: To provide a uniform means to access various *
- * types of device under RISC OS; *
- * *
- * Uses: stdio, string, stdarg, stdlib, werr, flex *
- * * *
- * History: *
- * <=0.09: prehistoric versions ;) *
- * 0.10: completed hooks for adding user-defined *
- * stream types *
- * 0.11: added code to ensure errors closing *
- * streams properly detected; added gserror() *
- * 0.12: fixed bug in gsread which read ~4Gbytes *
- * when asked for less bytes than in the *
- * stream's buffer *
- * 0.13: fixed a couple of bugs with internalr and *
- * internalw streams *
- * *
- * Conditions: *
- * You can produce and sell executable code from *
- * this module freely. The source code may be *
- * distributed freely, but not for profit. *
- * *
- * If you like these modules alot, and use them *
- * in your own programs, feel free to thank me *
- * with cash - 10 or more will get you a disc *
- * with the latest versions and new stream types. *
- * *
- * Disclaimer: *
- * This software is supplied in good faith, but I *
- * cannot accept liability for any loss incurred *
- * through the use or inability to use any part *
- * of this software. *
- * *
- * How to contact me: *
- * *
- * email: snail mail: *
- * sproven@cs.strath.ac.uk Simon Proven, *
- * Castle Cottage, *
- * Portencross, *
- * West Kilbride, *
- * KA23 9QA *
- * SCOTLAND *
- * *
- * Tel. (+44) 294 829 721 *
- * *
- *************************************************************/
-
- #include <stdio.h>
- #include <string.h>
- #include <stdarg.h>
- #include <stdlib.h>
-
- #include "werr.h"
- #include "gstream.h"
- #include "flex.h"
-
- #define FALSE 0
- #define TRUE 1
-
- /* this function tries to claim a flex block between maxsize
- * and minsize bytes - it will return the number of bytes
- * allocated, which may be zero
- */
-
- int gsgetbuf(flex_ptr anchor, size_t maxsize, size_t minsize)
- {
- while(maxsize>=minsize)
- {
- if (flex_alloc(anchor,maxsize))
- {
- return maxsize;
- }
-
- maxsize=((maxsize-minsize)>>1)+minsize;
- }
- return 0;
- }
-
- /* opens an internal stream for writing to. The stream's data is
- * stored internally in a flex block. Once the stream is closed,
- * the stream can be re-opened using gsopeninternalr to read the
- * information back again. The stream's data can be discarded
- * using gsdiscardinternal. The data remains intact after using
- * gsopeninternalr, so the stream can be closed and re-opened
- * to read the data over and over again.
- */
-
- int gsopeninternalw(gstream *stream)
- {
- stream->pos=0;
- stream->size=0;
- stream->type=INTERNAL_WRITE;
-
- if ((stream->bufsize=gsgetbuf(&(stream->buffer),gs_BUFSIZE,4))!=0)
- {
- stream->error=FALSE;
- return TRUE;
- }
- else
- {
- werr(FALSE,"Not enough space");
- return FALSE;
- }
- }
-
- int gsopeninternalr(gstream *stream)
- {
- stream->type=INTERNAL_READ;
- if (stream->buffer!=NULL)
- {
- stream->ungetsize=gsgetbuf(&(stream->ungetbuf),4,0);
- stream->ungetpos=0;
- stream->pos=0;
- stream->error=FALSE;
- return TRUE;
- }
- else
- return FALSE;
- }
-
- /* This function, when called for a write stream will cause all
- * buffered data to be passed to the stream's write driver for
- * sending to the host system.
- *
- * Currently, no action is performed on read streams - this may
- * change in future versions (to be more like fflush)
- */
-
- int gsflush(gstream *stream)
- {
- if (stream->error)
- return -1;
-
- if (stream->bufsize!=0)
- {
- switch(stream->type)
- {
- case WRITE:
- if (stream->io.write(stream->buffer,stream->size,(void *) stream)<stream->size)
- {
- stream->size=0;
- return -1;
- }
- stream->size=0;
- return 0;
-
- case INTERNAL_WRITE:
- return 0;
-
- default:
- return -1;
- }
- }
- else
- return 0;
- }
-
- /* this function will fill the buffer of a read stream from the
- * host environment, but only when the buffer is empty. At any
- * other time, the buffer contents will not be changed.
- *
- * If, after calling this function, stream->size is zero then it
- * may be assumed that there is no data remaining on the stream,
- * unless the stream is unbuffered.
- */
-
- static void gsfillbuf(gstream *stream)
- {
- if (stream->bufsize!=0)
- {
- switch(stream->type)
- {
- case READ:
- if (stream->pos==stream->size)
- {
- stream->size=stream->io.read(stream->buffer,stream->bufsize,(void *) stream);
- stream->pos=0;
- }
- break;
- default:
- break;
- }
- }
- }
-
- /* closes the given stream. Returns 0 on success or non-zero on error */
-
- int gsclose(gstream *stream)
- {
- int flushedok,closedok;
-
- switch(stream->type)
- {
- case WRITE:
- if (!stream->error) {
- flushedok=gsflush(stream);
- }
- closedok=stream->close(stream);
- if (stream->bufsize!=0)
- flex_free(&(stream->buffer));
- if (closedok && !flushedok || stream->error)
- return 0;
- else
- return -1;
- break;
-
- case READ:
- closedok=stream->close(stream);
- if (stream->ungetsize!=0)
- flex_free(&(stream->ungetbuf));
- if (stream->bufsize!=0)
- flex_free(&(stream->buffer));
- if (!closedok || stream->error)
- return -1;
- else
- return 0;
-
- case INTERNAL_WRITE:
- flex_extend(&(stream->buffer),stream->size);
- if (stream->error)
- return -1;
- else
- return 0;
-
- case INTERNAL_READ:
- if (stream->ungetsize!=0)
- flex_free(&(stream->ungetbuf));
- if (stream->error)
- return -1;
- else
- return 0;
- }
- return 0;
- }
-
- /* discards an internal stream's buffer contents */
-
- void gsdiscardinternal(gstream *stream)
- {
- flex_free(&(stream->buffer));
- }
-
- /* reads a character from the given stream. Returns EOF on end-of-file
- * or error
- */
-
- int gsgetc(gstream *stream)
- {
-
- if (stream->error)
- return EOF;
-
- if (stream->ungetpos==0)
- {
- switch (stream->type)
- {
- case READ:
- if (stream->bufsize!=0)
- {
- if (!(stream->pos<stream->size))
- {
- stream->size=0;
- stream->pos=0;
- gsfillbuf(stream);
- }
-
- if (stream->pos<stream->size)
- {
- return(((char *)stream->buffer)[stream->pos++]);
- }
- else
- return(EOF);
- }
- else
- {
- char c;
- if (gsread((void *) &c, 1, sizeof(char), stream)!=0)
- {
- return c;
- }
- else
- return EOF;
- }
- break;
-
- case INTERNAL_READ:
- if (stream->pos<stream->size)
- return(((char *)stream->buffer)[stream->pos++]);
- else
- return(EOF);
- default:
- return(EOF);
- }
- return(EOF);
- }
- else
- {
- if (stream->ungetpos<(stream->ungetsize>>1))
- {
- flex_extend(&(stream->ungetbuf),stream->ungetsize>>1);
- stream->ungetsize=stream->ungetsize>>1;
- }
- return ((char *)stream->ungetbuf)[--stream->ungetpos];
- }
- }
-
- /* puts a character to a given output stream. Returns the value of c or
- * EOF on error
- */
-
- int gsputc(int c, gstream *stream)
- {
- if (stream->error)
- return EOF;
-
- switch (stream->type)
- {
- case WRITE:
- if (stream->bufsize!=0)
- {
- if (!(stream->size<stream->bufsize))
- {
- if (gsflush(stream)<0)
- return EOF;
- }
- ((char *)stream->buffer)[stream->size++]=c;
- return c;
- }
- else
- {
- char d;
- d=c;
- if (gswrite((void *) &d, 1, sizeof (char), stream)==1)
- return c;
- else
- return EOF;
- }
- case INTERNAL_WRITE:
- if (stream->size==stream->bufsize)
- {
- int increment=gs_BUFSIZE;
- while(!flex_extend(&(stream->buffer),stream->bufsize+increment))
- {
- increment=increment/2;
- }
- if (increment==0)
- {
- werr(FALSE,"Not enough space");
- stream->error=TRUE;
- return EOF;
- }
- else
- stream->bufsize+=increment;
- }
- ((char *)stream->buffer)[stream->size++]=c;
- return c;
- default:
- return EOF;
- }
- }
-
- /* internal function which writes data to an internal stream - NEVER
- * call this function yourself, always use gswrite
- */
-
- static size_t writeinternal(void *ptr, size_t nbytes, gstream *stream)
- {
- if (stream->bufsize<(stream->size+nbytes))
- {
- if (!flex_extend(&(stream->buffer),stream->size+nbytes))
- {
- werr(FALSE,"Not enough space");
- stream->error=TRUE;
- return 0;
- }
- else
- stream->bufsize=stream->size+nbytes;
- }
- memcpy((char *)stream->buffer+stream->size,(char *) ptr,nbytes);
- stream->size+=nbytes;
- return nbytes;
- }
-
- /* writes stuff to a given stream. identical in behaviour to stio's
- * fwrite()
- */
-
- size_t gswrite(void *ptr, size_t size, size_t nmemb, gstream *stream)
- {
- size_t nbytes=size*nmemb;
- int i;
-
- if (stream->error)
- return 0;
-
- switch (stream->type)
- {
- case WRITE:
- if (stream->bufsize!=0)
- {
- if ((stream->bufsize-stream->size)>=nbytes)
- {
- memcpy((char *)stream->buffer+stream->size,(char *) ptr, nbytes);
- stream->size+=nbytes;
- return nbytes/size;
- }
- else
- {
- gsflush(stream);
- if ((stream->bufsize-stream->size)>=nbytes)
- {
- memcpy((char *)stream->buffer+stream->size,(char *) ptr, nbytes);
- stream->size+=nbytes;
- return nbytes/size;
- }
- else
- return(stream->io.write(ptr,nbytes,(void *)stream))/size;
- }
- }
- else
- return(stream->io.write(ptr,nbytes,(void *)stream))/size;
- case INTERNAL_WRITE:
- return(writeinternal(ptr,nbytes,stream))/size;
- default:
- for (i=0;i<nbytes;i++)
- if (gsputc(((char *)ptr)[i],stream)==EOF)
- return i/size;
- return(nbytes)/size;
- }
- }
-
- /* internal function to read bytes from an internal read stream - NEVER
- * call this yourself, always use gswrite()
- */
-
- static size_t readinternal(char *ptr, size_t nbytes, gstream *stream)
- {
- if ((stream->size-stream->pos)>=nbytes)
- {
- memcpy(ptr,(char *)stream->buffer+stream->pos,nbytes);
- stream->pos+=nbytes;
- return nbytes;
- }
- else
- {
- int r=stream->size-stream->pos;
- memcpy(ptr,(char *)stream->buffer+stream->pos,r);
- stream->pos+=r;
- return r;
- }
- }
-
- /* equivalent of fread() */
-
- size_t gsread(void *ptr, size_t size, size_t nmemb, gstream *stream)
- {
- size_t nbytes=size*nmemb;
- int i,b,p;
-
- if (size==0 || nmemb==0 || stream->error)
- return 0;
-
- if (stream->ungetpos==0)
- {
- switch (stream->type)
- {
- case READ:
- if (stream->bufsize==0)
- return (stream->io.read(ptr,nbytes,(void *) stream))/size;
- else if (stream->size==stream->pos)
- {
- gsfillbuf(stream);
- if (stream->size==stream->pos)
- return 0;
- }
- p=stream->pos;
- b=stream->size-p;
- if (nbytes>=b)
- {
- memcpy((char *)ptr,(char *)stream->buffer+p,b);
- stream->size=0;
- stream->pos=0;
- b+=stream->io.read((void *) ((char *)ptr+b),nbytes-b,(void *) stream);
- return b/size;
- }
- else
- {
- memcpy((char *)ptr,(char *)stream->buffer+p,nbytes);
- stream->pos+=nbytes;
- return nbytes/size;
- }
- break;
-
- case INTERNAL_READ:
- return(readinternal(ptr,nbytes,stream))/size;
-
- default:
- return 0;
- }
- }
- else
- {
- int c;
- for (i=0;i<nbytes;i++)
- {
- c=gsgetc(stream);
- if (c==EOF)
- return i/size;
- ((char *)ptr)[i]=c;
- }
- return nbytes/size;
- }
- }
-
- /* equivalent to fputs() */
-
- int gsputs(const char *s, gstream *stream)
- {
- size_t l=strlen(s);
-
- if (stream->error)
- return EOF;
-
- /* this line untested - I've just swapped round the
- * l and sizeof to remove a bug
- */
- if (l==gswrite((void *)s,sizeof(char),l,stream))
- return 0;
- else
- return EOF;
- }
-
- /* equivalent to fgets() */
-
- char *gsgets(char *s, int n, gstream *stream)
- {
- int l=0,c;
-
- if (stream->error)
- return (NULL);
-
- while (l<(n-1) && (c=gsgetc(stream))!=EOF)
- {
- s[l++]=c;
- if (c=='\n')
- {
- s[l]='\0';
- return(s);
- }
- }
-
- if (l==0)
- return(NULL);
-
- s[l]='\0';
- return(s);
- }
-
- /* equivalent of fprintf */
-
- int gsprintf(gstream *stream, const char *format, ...)
- {
- va_list p;
- char buffer[gs_PRINTFMAX+1];
- int l;
- size_t w;
-
- if (stream->error)
- return -1;
-
- va_start(p,format);
-
- l=vsprintf(buffer,format,p);
-
- va_end(p);
-
- /* swapped l and sizeof, untested, as in gsputs */
- w=gswrite((void *)buffer,sizeof(char),l,stream);
-
- if (w==l)
- return(l);
- else
- return -1;
- }
-
- /* equivalent of ungetc() */
-
- int gsungetc(int c, gstream *stream)
- {
-
- if (c==EOF || stream->error)
- return EOF;
-
-
- /* first of all we check if the unget buffer is
- * large enough and if not we extend it. To do
- * this we attempt to double its size and if that
- * doesn't work then we try halving the increase
- * in size until we get a buffer size that will
- * fit into availabe memory.
- */
-
- if (stream->ungetsize==stream->ungetpos)
- {
-
- if (stream->ungetsize==0)
- {
- if (flex_alloc(&(stream->ungetbuf),4))
- {
- stream->ungetsize=4;
- }
- }
- else
- {
- int i=stream->ungetsize;
- while (i!=0)
- {
- if (flex_extend(&(stream->ungetbuf),i+stream->ungetsize))
- {
- stream->ungetsize+=i;
- break;
- }
- i=i>>1;
- }
- }
- }
-
- /* now we have made our attempt to claim a buffer. We
- * now try to put the data in the unget buffer
- */
-
- if (stream->ungetsize>(stream->ungetpos))
- {
- ((char *)stream->ungetbuf)[stream->ungetpos++]=c;
- }
- else
- {
- werr(FALSE,"gsungetc buffer full");
- stream->error=TRUE;
- return EOF;
- }
- return c;
- }
-
- /* this function returns true if the next gsgetc on the
- * stream will return EOF
- */
-
- int gseof(gstream *stream)
- {
- int c;
-
- if ((c=gsgetc(stream))==EOF)
- return TRUE;
-
- gsungetc(c,stream);
- return FALSE;
- }
-
- int gserror(gstream *stream)
- {
- return stream->error;
- }
-
-